﻿using Nemerle.Assertions;
using Nemerle.Collections;
using Nemerle.Text;
using Nemerle.Utility;
using Nemerle.ComputationExpressions;
using Nemerle.ComputationExpressions.Async;

using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;

namespace Nemerle2.Compiler
{
  /// <summary>
  /// http://code.google.com/p/nemerle-2/wiki/NSolution
  /// </summary>
  public class NSolution
  {
    _assemblyMetadataProvider : IMetadataProvider;
    _projectMetadataProvider  : IMetadataProvider;
    _executionContext : ExecutionContext = SystemExecutionContexts.ThreadPool();

    public this([NotNull] assemblyMetadataProvider : IMetadataProvider)
    {
      _assemblyMetadataProvider = assemblyMetadataProvider;
      _projectMetadataProvider = ProjectLoader(this);
    }
    
    public StandardTypes : StandardTypes
    {
      get{ throw NotImplementedException() }
    }

    //public OpenedAssemblesAndProjects : array[Async[IReferencedAssembly]] { get { lock (_assemblyMap) _assemblyMap.Values.NToArray() } }

    /// Request to load a assembly (or a project) for given path. After the assembly will be loaded the callback has been called.
    public LoadRequest([NotNull] assemblyOrProjectPaths : Seq[string]) : Seq[IReferencedAssembly]
    {
      def GetKind(path)
      {
        def ext = Path.GetExtension(path).ToLower();

        match (ext)
        {
          | ".nproj"        => ProjectKind.Nemerle
          | ".dll" | ".exe" => ProjectKind.Assembly
          | _               => ProjectKind.Unknown
        }
      }
      
      def Assemblies = _assemblyMetadataProvider.LoadAssemblies(assemblyOrProjectPaths.FilterLazy(p => GetKind(p) == ProjectKind.Assembly));
      def projects   = _projectMetadataProvider .LoadAssemblies(assemblyOrProjectPaths.FilterLazy(p => GetKind(p) == ProjectKind.Nemerle ));
      
      def eAssemblies = Assemblies.GetEnumerator();
      def eProjects   = projects.GetEnumerator();
      def ret         = List();
      
      foreach(path in assemblyOrProjectPaths)
      {
        def addToList(enumer, lst)
        {
          assert(enumer.MoveNext(), "provider returned incrorrect assembly count");
          lst.Add(enumer.Current);
        }
        match (GetKind(path))
        {
          | ProjectKind.Nemerle  => addToList(eProjects, ret)
          | ProjectKind.Assembly => addToList(eAssemblies, ret)
          | ProjectKind.Unknown  => throw ArgumentException($<#Unsuported file "$path". Expected: ".nproj", ".dll" or ".exe".#>);
        }
      }
      
      ret
    }
    
    private enum ProjectKind {|Unknown| Nemerle| Assembly}
  }
}
